<template>
<div class="local-machine rounded-borders bg-white overflow-hidden">
  <q-splitter
    v-model="splitterModel"
    :limits="[350, 600]"
    unit="px"
    class="splitter"
  >
    <template v-slot:before>
      <q-splitter
        v-model="insideModel1"
        horizontal reverse
      >
        <template v-slot:before>
          <local-dir-tree @getFile="getFileLocal" ref="localDirTree"></local-dir-tree>
        </template>
        <template v-slot:after>
          <machine-dir-tree
            :client="client" :request-id="requestId" @onSubscribe="onSubscribe"
            :send-status="sendStatus"
            @getFile="getFileMachine"
            @setSendStatus="setSendStatus"
            @setMachineTickNodes="setMachineTickNodes"
            ref="machineDirTree" />
        </template>
      </q-splitter>
    </template>

    <template v-slot:after>
      <q-splitter
        v-model="insideModel1"
        horizontal reverse
      >
        <template v-slot:before>
          <file-list :sele-files="files" :node-act="nodeAct" @infoDir="infoDir" @back="backParent"
                     :sibling-node="machineNodeAct" :stat-pos="statPos" ref="fileList"
                     @setDragItem="setDragItem" :drag-item="dragItem" @showCompareList="showCompareList"
                     @addProgressFiles="addProgressFiles" @refreshSelFile="refreshSelFile"
                     :client="client"
                     @onSubscribe="onSubscribe" @onSubscribe1="onSubscribe1" @onSubscribe2="onSubscribe2"></file-list>
        </template>
        <template v-slot:after>
          <machine-file-list :sele-files="machineFiles" :node-act="machineNodeAct" @infoDir="infoDirMachine"
                             :send-status="sendStatus" :machineTickNodes="machineTickNodes"
                             :sibling-node="nodeAct" :stat-pos="statPos" ref="machineFileList"
                             @refreshInfo="refreshMachineInfo"
                             @back="backParentMachile" @setDragItem="setDragItem" :drag-item="dragItem"
                             @showCompareList="showCompareList" @addProgressFiles="addProgressFiles"
                             @refreshSelFile="refreshSelFile"
                             :client="client" :request-id="requestId"
                             @onSubscribe="onSubscribe" @onSubscribe1="onSubscribe1" @onSubscribe2="onSubscribe2"></machine-file-list>
        </template>
      </q-splitter>
    </template>

  </q-splitter>
  <div class="file-bottom">
    <div class="stat">选中{{actFile.dirs.length}}个文件夹和{{actFile.files.length}}个文件，文件大小共{{sizeFormat(actFile.fileSize)}}</div>
    <div class="progressResult text-primary text-underline cursor-pointer"
         v-stat="{id:CommonConfig.statData[statPos].record.id,name:CommonConfig.statData[statPos].record.name}"
         @click="showProgressResult">
      传输记录：正在传输（{{progressFiles.length}}）； 传输成功（{{successProgressFiles.length}}）； 传输失败（{{failProgressFiles.length}}）</div>
  </div>
  <!--  文件对比列表-->
  <compare-file ref="CompareFile" :drag-item="dragItem" :stat-pos="statPos" @compare="compareDetail"></compare-file>
  <!--  文件内容对比查看-->
  <code-merge ref="codeMerge"></code-merge>
  <!--  右下角正在上传的弹框-->
  <progress-dialog ref="progressDialog" :list="progressFiles"></progress-dialog>
  <!--  文件传输记录-->
  <progress-result-dialog ref="progressResult" :doing-data="doingProgressFiles"
                          :success-data="successProgressFiles" :fail-data="failProgressFiles"
                          @delOneRecord="delOneRecord" @clearRecord="clearRecord"></progress-result-dialog>
</div>
</template>

<script>
import LocalDirTree from './components/local-dir-tree'
import FileList from './components/file-list'
import MachineDirTree from './components/machine-dir-tree'
import MachineFileList from './components/machine-file-list'
import CompareFile from './components/compare-file'
import CodeMerge from 'src/components/code-merge'
import ProgressDialog from './components/progress-dialog'
import ProgressResultDialog from './components/progress-result-dialog'
import { readFile as readFileCloud, addProgressRecord } from 'src/boot/program/cloud'
import { cncToBox, fileMachineLocal } from 'src/boot/program/machine'
import { readFile as readFileLocal } from 'src/assets/js/file-dir'
import { matchFileType } from 'src/assets/js/util'
import { saveProgressSuccessRecord, loadProgressSuccessRecord, saveProgressFailRecord,
  loadProgressFailRecord, clearProgressSuccessRecord, clearProgressFailRecord } from 'src/assets/js/storage'
import boxErr from 'src/assets/json/boxErr'
import boxCodeErr from 'src/assets/json/boxCodeErr'
import { mapGetters } from 'vuex'
import refreshMixin from '../../assets/js/refresh-mixin'
export default {
  name: 'local-machine',
  mixins: [refreshMixin],
  data () {
    return {
      splitterModel: 35, // 竖向占比
      insideModel1: 50, // 横向左侧占比
      insideModel2: 50, // 横向右侧占比
      //  mqtt实例
      requestId: '', // 唯一的userId用于连接盒子，此id必须唯一
      client: null,
      boxId: '', // 当前订阅的boxId
      programId: '', // 传输文件，当前订阅的requestId
      programId1: '', // 传输文件，当前订阅的requestId
      // 本地
      isSupportReadLoacal: false, // 是否支持本地文件读取
      localMessage: '当前设备不支持本地文件读取', // 是否支持本地文件读取提示
      localId: 0, // 存储id
      files: [], // 本地文件和文件夹列表
      nodeAct: { path: '', label: '' }, // 左侧当前选中的位置
      // 机床
      sendStatus: 'single', // 发送多台many 发送单台 single
      machineFiles: [], // 本地文件和文件夹列表
      machineNodeAct: { path: '', label: '' }, // 左侧当前选中的位置
      machineTickNodes: [], // 左侧勾选的数据
      dragItem: [], // 存储拖动的数据
      progressFiles: [], // 正在传输的文件
      doingProgressFiles: [], // 传输成功的列表
      successProgressFiles: [], // 传输成功的列表
      failProgressFiles: [], // 传输失败的列表
      progressTime: null, // 存储定时器
      actFile: {
        dirs: [],
        files: [],
        fileSize: 0
      },
      compareList: [], // 文件对比数据缓存
      statPos: 'localDevice' // 定义埋点位置
    }
  },
  computed: {
    ...mapGetters(['userInfo'])
  },
  created () {
    this.initMqtt()
    this.successProgressFiles = this.sortRecord(loadProgressSuccessRecord(this.userInfo.tenant_id))
    this.failProgressFiles = this.sortRecord(loadProgressFailRecord(this.userInfo.tenant_id))
  },
  methods: {
    /**
     * 全局刷新数据
     * author: xueli
     */
    refreshInfo () {
      this.$refs.localDirTree.initInfo()
      this.$refs.machineDirTree.initInfo()
    },
    setSendStatus (status) {
      this.sendStatus = status
    },
    // 初始化mqtt
    initMqtt () {
      // 生成一个唯一的userId用于连接盒子，此id必须唯一
      this.requestId = 'webClient' + this.userInfo.user_id.toString() + Date.parse(new Date()).toString()
      // 创建mq实例
      this.client = new window.Paho.MQTT.Client(
        this.CommonConfig.mqttConfig.MQTT_SERVICE,
        Number(this.CommonConfig.mqttConfig.MQRR_PORT),
        // '192.168.1.193',
        // 8083,
        this.requestId)
      // 连接服务器并注册连接成功处理事件
      let self = this
      this.client.connect({
        keepAliveInterval: 30,
        onSuccess: this.onConnect,
        onFailure: function (message) { // 连接失败
          console.log('连接失败', JSON.stringify(message))
          // 3秒后重连
          setTimeout(() => {
            self.initMqtt()
          }, 3000)
        }
      })
      // 注册连接断开处理事件
      this.client.onConnectionLost = this.onConnectionLost
      // 注册消息接收处理事件
      this.client.onMessageArrived = this.onMessageArrived
    },
    onConnect () {
      console.log('注册连接成功')
      this.onSubscribeAll()
    },
    // 订阅并发送消息,订阅主题是获取盒子列表，删除，重命名
    onSubscribe (boxId) {
      this.boxId = boxId
      this.client.subscribe(
        `${this.CommonConfig.mqttConfig.MQTT_TOPIC.machineFileInfo.subscribeTopic}${this.boxId}`,
        { qos: 2 }
      )
      // if (this.boxId.length === 0) {
      //   this.boxId = boxId
      //   // 订阅主题
      //   this.client.subscribe(
      //     `${this.CommonConfig.mqttConfig.MQTT_TOPIC.machineFileInfo.subscribeTopic}${this.boxId}`,
      //     { qos: 2 }
      //   )
      // } else if (this.boxId !== boxId) {
      //   // 取消订阅
      //   // this.client.unsubscribe(
      //   //   `${this.CommonConfig.mqttConfig.MQTT_TOPIC.machineFileInfo.subscribeTopic}${this.boxId}`,
      //   //   { qos: 2 }
      //   // )
      //   this.boxId = boxId
      //   // 订阅主题
      //   this.client.subscribe(
      //     `${this.CommonConfig.mqttConfig.MQTT_TOPIC.machineFileInfo.subscribeTopic}${this.boxId}`,
      //     { qos: 2 }
      //   )
      // }
    },
    // 订阅并发送消息,订阅主题是获取上传进度
    onSubscribe1 (programId) {
      if (this.programId.length === 0 || this.programId !== programId) {
        this.programId = programId
        // 订阅主题
        this.client.subscribe(
          `${this.CommonConfig.mqttConfig.MQTT_TOPIC.machineFileSendInfo.subscribeTopic}${this.programId}`,
          { qos: 2 }
        )
      }
    },
    // 订阅并发送消息,订阅主题是外部存储发送结果
    onSubscribe2 (programId) {
      if (this.programId1.length === 0 || this.programId1 !== programId) {
        this.programId1 = programId
        // 订阅主题
        this.client.subscribe(
          `${this.CommonConfig.mqttConfig.MQTT_TOPIC.machineFileSendResult.subscribeTopic}${this.programId1}`,
          { qos: 2 }
        )
      }
    },
    // 链接断开重连时，重新订阅
    onSubscribeAll  () {
      console.log('重新连接成功')
      if (this.boxId.length > 0) {
        this.client.subscribe(
          `${this.CommonConfig.mqttConfig.MQTT_TOPIC.machineFileInfo.subscribeTopic}${this.boxId}`,
          { qos: 2 }
        )
      }
      if (this.programId.length > 0) {
        this.client.subscribe(
          `${this.CommonConfig.mqttConfig.MQTT_TOPIC.machineFileSendInfo.subscribeTopic}${this.programId}`,
          { qos: 2 }
        )
      }
      if (this.programId1.length > 0) {
        this.client.subscribe(
          `${this.CommonConfig.mqttConfig.MQTT_TOPIC.machineFileSendResult.subscribeTopic}${this.programId1}`,
          { qos: 2 }
        )
      }
    },
    // 取消订阅
    unSubscribe (boxId) {
      this.client.unsubscribe(
        `${this.CommonConfig.mqttConfig.MQTT_TOPIC.machineFileInfo.subscribeTopic}${this.boxId}`
      )
    },
    onMessageArrived (message) {
      console.log(message)
    },
    // 连接丢失
    onConnectionLost (responseObject) {
      console.log('连接断开事件')
      if (responseObject.errorCode !== 0) {
        console.log('onConnectionLost:' + responseObject.errorMessage)
        console.log('连接已断开')
      }
      let self = this
      setTimeout(() => {
        self.initMqtt()
      }, 3000)
    },
    // 本地
    // 根据左侧目录树获取右侧目录与文件列表
    getFileLocal (files, node) {
      this.files = files
      this.nodeAct = node || { path: '', label: '' }
    },
    // 通过右侧选择目录，左侧同步打开目录位置
    infoDir (selFiles, node, dirs) {
      this.files = node
      this.nodeAct = JSON.parse(JSON.stringify(selFiles)) || { path: '', label: '' }
      let dirs2 = dirs.map(item => {
        return {
          ...item,
          lazy: true,
          isFile: false,
          source: 'local',
          children: []
        }
      })
      this.nodeAct.children = dirs2
      this.$refs.localDirTree.getFile(selFiles)
      this.$refs.localDirTree.setExpand(selFiles, dirs)
    },
    // 返回上一级
    backParent () {
      this.$refs.localDirTree.backParentNode()
    },
    // 机床
    // 根据左侧目录树获取右侧目录与文件列表
    getFileMachine (files, node) {
      this.machineFiles = JSON.parse(JSON.stringify(files))
      // this.$refs.machineFileList.setFile(files)
      this.machineNodeAct = node || { path: '', label: '' }
    },
    // 通过右侧选择目录，左侧同步打开目录位置
    infoDirMachine (selFiles, node, dirs) {
      this.machineFiles = node
      this.machineNodeAct = JSON.parse(JSON.stringify(selFiles)) || { path: '', label: '' }
      let dirs2 = dirs.map(item => {
        return {
          ...item,
          lazy: true,
          isFile: false,
          source: 'cloud',
          children: []
        }
      })
      this.machineNodeAct.children = dirs2
      this.$refs.machineDirTree.getFile(selFiles)
      this.$refs.machineDirTree.setExpand(selFiles, dirs)
    },
    // 更新数据
    refreshMachineInfo () {
      this.$refs.machineDirTree.getFile(this.machineNodeAct)
    },
    // 返回上一级
    backParentMachile () {
      this.$refs.machineDirTree.backParentNode()
    },
    // 选择勾选数据
    setMachineTickNodes (tickNodes) {
      this.machineTickNodes = tickNodes
    },
    // 公共
    // 存储拖动数据
    setDragItem (dragItem) {
      console.log(dragItem)
      this.dragItem = dragItem
    },
    // 获取拖动数据
    getDragItem (dragItem) {
      return this.dragItem
    },
    // 显示加入对比列表
    showCompareList (file) {
      this.$refs.CompareFile.show(file)
    },
    // 读取文件内容，弹出比较弹框
    compareDetail (compareList) {
      console.log(compareList)
      const allow = this.allowSize(compareList)
      if (!allow.allow) {
        this.errMessage(allow.message)
        return false
      }
      this.$q.loading.show()
      this.readFile(compareList)
    },
    // 比较文件的大小是否符合规则
    allowSize (compareList) {
      let result = { message: '', allow: true }
      for (let i = 0; i < compareList.length; i++) {
        if (matchFileType(compareList[i].label)) {
          result = { message: '无法读取图片文件、word文件、视频文件、音频文件、快捷方式、压缩文件！', allow: false }
          return result
        }
        if (compareList[i].source === 'local' && compareList[i].size > 5 * 1024 * 1024) {
          result = { message: '无法读取超过5M的本地文件', allow: false }
          return result
        }
        if (compareList[i].source === 'machine' && compareList[i].size > 1024 * 1024) {
          result = { message: '无法读取超过1M的机床文件', allow: false }
          return result
        }
      }
      return result
    },
    // 读取对比文件的内容
    readFile (compareList) {
      this.compareList = compareList
      let localArr = compareList.filter(item => item.source === 'local')
      // 两个都是本地文件的对比
      if (localArr.length === 2) {
        readFileLocal(localArr[0].path,
          () => {
            this.$q.loading.hide()
            this.errMessage(`文件${localArr[0].label}读取失败`)
          },
          (str, decode) => {
            // 本地文件内容
            let localContent1 = {
              title: localArr[0].label,
              content: str
            }
            readFileLocal(localArr[1].path,
              () => {
                this.$q.loading.hide()
                this.errMessage(`文件${localArr[1].label}读取失败`)
              },
              (str, decode) => {
                // 本地文件内容
                let localContent2 = {
                  title: localArr[1].label,
                  content: str
                }
                this.$refs.codeMerge.showDialog(localContent1, localContent2)
                this.$refs.CompareFile.hide()
                this.$q.loading.hide()
              })
          })
      } else if (localArr.length === 1) {
        // 一个本地一个机床文件的对比
        let localItem = compareList.find(item => item.source === 'local')
        readFileLocal(localItem.path,
          () => {
            this.$q.loading.hide()
            this.errMessage(`文件${localItem.label}读取失败`)
          },
          (str, decode) => {
            // 本地文件内容
            let localContent = {
              title: localItem.label,
              content: str
            }
            let machineItem = compareList.find(item => item.source === 'machine')
            this.readMachineFile(machineItem, localContent)
          })
      } else if (localArr.length === 0) {
        // 两个都为机床文件的对比
        let machineItem = compareList[0]
        this.readMachineFile(machineItem)
      }
    },
    // 读取设备端文件
    readMachineFile (item, prevContent) {
      if (item.position === 'cnc') {
        this.putMachineInside(item, prevContent)
      } else {
        this.putMachine(item, item.path, prevContent)
      }
    },
    // 开始发送内部存储文件到外部存储文件
    putMachineInside (send, prevContent) {
      let requestId1 = 'webClient' + this.userInfo.user_id.toString() + Date.parse(new Date()).toString()
      // let self = this
      let params1 = {
        boxId: send.boxId,
        cncPath: send.path,
        requestId: requestId1
      }
      this.$emit('onSubscribe', send.boxId)
      cncToBox(params1)
      this.client.onMessageArrived = (message) => {
        let data = JSON.parse(message.payloadString)
        try {
          // 这里是放置对其他消息进行处理
          if (requestId1 === data.request_id && data.hasOwnProperty('error')) {
            if (data.error === 0) {
              this.putMachine(send, data.file_path, prevContent)
            } else {
              this.$q.loading.hide()
              this.errMessage(`文件内容读取失败！${boxErr[data.code]}`)
            }
          }
        } catch (e) {
          console.error(e)
          this.errMessage('文件内容读取失败！')
        }
      }
    },
    // 从机床传输到云端的方法
    putMachine (send, path, prevContent) {
      // let self = this
      let params = {
        boxId: send.boxId,
        fileName: send.label,
        filePath: path,
        fromBox: send.position === 'box' ? 1 : 0
      }
      let requestId
      fileMachineLocal(params).then(res => {
        requestId = res.data.requestId
        // cloudDownloadUrl = res.data.url
        this.onSubscribe2(res.data.requestId)
      })
      this.client.onMessageArrived = (message) => {
        let data = JSON.parse(message.payloadString)
        try {
          // 这里是放置对其他消息进行处理
          if (requestId === data.request_id) {
            if (data.hasOwnProperty('source') && data.source === 'send_result') {
              if (data.code === 200) {
                // 盒子上传到云端已完成，接下来是从云端获取内容
                this.getFileContent(send, data.url, prevContent)
              } else {
                this.$q.loading.hide()
                this.errMessage(`文件${send.label}读取失败！${boxCodeErr[data.code]}`)
              }
            }
          }
        } catch (e) {
          console.error(e)
          this.errMessage('文件内容读取失败')
        }
      }
    },
    // 通过云端接口读取云端文件内容
    getFileContent (send, sendPath, prevContent) {
      readFileCloud(sendPath).then(res => {
        let machineContent = {
          title: send.label,
          content: res.data
        }
        if (prevContent) {
          this.$refs.codeMerge.showDialog(prevContent, machineContent)
          this.$refs.CompareFile.hide()
          this.$q.loading.hide()
        } else {
          let machineItem = this.compareList[1]
          this.readMachineFile(machineItem, machineContent)
        }
      }).catch(() => {
        this.$q.loading.hide()
      })
    },
    // 失败提示
    errMessage (message) {
      this.$q.dialog({
        title: `<p class="dialog-notice-title">
<i class="iconfont icontubiao-52" ></i> 提示</p>`,
        message: message,
        html: true,
        ok: {
          unelevated: true,
          label: '确认'
        }
      })
    },
    // 显示发送确认弹框
    showSendSure () {
      this.$refs.sendSure.showDialog()
    },
    // 添加上传中和上传完成的文件
    addProgressFiles (file) {
      // 添加传输目录
      const index = this.progressFiles.findIndex(item => {
        return item.name === file.name && item.acceptPath === file.acceptPath && item.acceptor === file.acceptor
      })
      this.$refs.progressDialog.showDialog()
      // 开始传输
      if (index === -1) {
        this.progressFiles.unshift(file)
      } else {
        this.progressFiles[index].progress = file.progress
        this.progressFiles[index].useTime = file.useTime
        this.progressFiles[index].state = file.state
      }
      // 传输失败
      if (file.state === 'fail') {
        this.failProgressFiles.unshift(file)
        saveProgressFailRecord(this.userInfo.tenant_id, this.failProgressFiles)
      } else if (file.state === 'success') {
        // 传输成功
        this.successProgressFiles.unshift(file)
        saveProgressSuccessRecord(this.userInfo.tenant_id, this.successProgressFiles)
        if (file.acceptor === 'cloud') {
          let params = {
            fileName: file.acceptPath,
            size: file.fileSize
          }
          // 告诉服务器给云端增加个版本
          addProgressRecord(params)
        }
      }
      this.doingProgressFiles = this.progressFiles.filter(item => {
        return item.state !== 'fail' && item.state !== 'success'
      })
      this.isOverProgress()
    },
    // 判断所有文件是否上传完成，完成择1.5s后消失
    isOverProgress () {
      if (this.doingProgressFiles.length === 0) {
        this.progressTime = setTimeout(() => {
          this.$refs.progressDialog.hideDialog()
          this.progressFiles = []
        }, 1500)
      } else {
        clearTimeout(this.progressTime)
      }
    },
    // 显示传输记录弹框
    showProgressResult () {
      this.$refs.progressResult.showDialog()
    },
    // 删除一条传输记录
    delOneRecord (state, file) {
      if (state === 'success') {
        this.successProgressFiles = this.successProgressFiles.filter(item => {
          return !(item.name === file.name && item.startTime === file.startTime)
        })
        saveProgressSuccessRecord(this.userInfo.tenant_id, this.successProgressFiles)
      }
      if (state === 'fail') {
        this.failProgressFiles = this.failProgressFiles.filter(item => {
          return item.name !== file.name && item.startTime !== file.startTime
        })
        saveProgressFailRecord(this.userInfo.tenant_id, this.failProgressFiles)
      }
    },
    // 清空传输记录
    clearRecord (state) {
      if (state === 'success') {
        this.successProgressFiles = []
        clearProgressSuccessRecord(this.userInfo.tenant_id, this.successProgressFiles)
      }
      if (state === 'fail') {
        this.failProgressFiles = []
        clearProgressFailRecord(this.userInfo.tenant_id, this.failProgressFiles)
      }
    },
    refreshSelFile (source, actFile) {
      this.actFile.files = []
      this.actFile.dirs = []
      actFile.forEach(item => {
        if (item.isFile) {
          this.actFile.files.push(item)
        } else {
          this.actFile.dirs.push(item)
        }
      })
      this.actFile.fileSize = this.actFile.files.reduce((prev, cur) => {
        return prev + Number(cur.size)
      }, 0)
      if (source === 'local') {
        this.$refs.machineFileList.clearActFile1()
      } else if (source === 'machine') {
        this.$refs.fileList.clearActFile1()
      }
    },
    // 文件大小格式化
    sizeFormat (size) {
      if (size === 0) {
        return '—'
      } else if (size / 1024 < 1) {
        return size + 'B'
      } else if (size / 1024 / 1024 < 1) {
        return (size / 1024).toFixed(2) + 'K'
      } else if (size / 1024 / 1024 / 1024 < 1) {
        return (size / 1024 / 1024).toFixed(2) + 'M'
      } else {
        return (size / 1024 / 1024 / 1024).toFixed(2) + 'G'
      }
    },
    // 传输记录按照时间排序
    sortRecord (record) {
      let newRecord = record.sort((a, b) => {
        if (a.endTime < b.endTime) {
          return 1
        } else if (a.endTime > b.endTime) {
          return -1
        } else return 0
      })
      newRecord = newRecord.map(item => {
        return {
          ...item,
          acceptPath: item.acceptPath.replace('//', '/')
        }
      })
      return newRecord
    }
  },
  components: {
    LocalDirTree,
    FileList,
    MachineDirTree,
    MachineFileList,
    CompareFile,
    CodeMerge,
    ProgressDialog,
    ProgressResultDialog
  }
}
</script>

<style scoped lang="stylus">
  .splitter{
    height calc(100% - 30px)
  }
  .file-bottom{
    display flex
    justify-content space-between
    padding 0 20px
    height 30px
    line-height 30px
    border-top 1px solid #d3d3d3
    .text-underline{
      text-decoration underline
    }
  }
  </style>
  <style lang="stylus">
  .local-cloud{
    .renameInput.q-field--outlined .q-field__control{
      padding: 0
      padding-left 5px
    }
  }
</style>
